import {parse} from 'querystring';
import config from "@/config";
import dicService from '@/pages/sys/dic/service'
import qs from "qs";
import moment from "moment";
import {SysUserModel} from "@/pages/sys/user/data";
import {DicValueType, DndNodeModel, TreeNodeModel} from "@/global";
import React, {Dispatch, SetStateAction} from "react";
import {ApiResult} from "@/utils/data";
import {SysModuleModel} from "@/pages/sys/module/data";
import {request} from "@/utils/request";

/* eslint no-useless-escape:0 import/prefer-default-export:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;

/** 检查path是否是http地址 */
export const isUrl = (path: string): boolean => reg.test(path);

/** 获取页面Url参数 */
export const getPageQuery = () => parse(window.location.href.split('?')[1]);

/** 获取登录页面背景图片样式 */
export const getLoginBackgroundStyle = () => {
  let randomNum = Math.floor(Math.random() * 24 + 1);
  let path = config.cdn + '/bg/bg' + randomNum + '.jpg';
  return {
    background: 'url(' + path + ') center center/100% 100% no-repeat'
  };
}

/** 当前用户对象 */
let currentUser: SysUserModel;
/** 当前用户拥有的模块对象 */
let currentUserOwnModules: Map<string, SysModuleModel>;

/** 设置当前用户 */
export function setCurrentUser(user: SysUserModel | null) {
  if (user === null) {
    localStorage.removeItem('xci-antd-pro-current-user');
  } else {
    currentUser = user;
    localStorage.setItem('xci-antd-pro-current-user', user && JSON.stringify(user));
  }
}

/** 获取当前用户 */
export function getCurrentUser(): SysUserModel {
  if (!currentUser) {
    const userJson = localStorage.getItem('xci-antd-pro-current-user');
    if (userJson) {
      currentUser = JSON.parse(userJson);
    }
  }
  return currentUser || {};
}

/** 获取用户token */
export function getUserToken(): string {
  const user = getCurrentUser();
  return user.token || '';
}

/** 获取当前用户 */
export function getCurrentUserId(): string {
  const user = getCurrentUser();
  return user.id || '';
}

/**
 * 判断当前登录用户是否是管理员
 */
export function isAdminUser(): boolean {
  const user = getCurrentUser();
  return user.admin || false;
}

/** 检测当前用户是否拥有指定编码的权限 */
export function hasPermission(permissionCode: string) {
  const user = getCurrentUser();
  if (user && user.admin) {
    return true;
  }
  if (!permissionCode) return false;
  if (!currentUserOwnModules) {
    currentUserOwnModules = new Map<string, SysModuleModel>();
    user.permission.modules && user.permission.modules.forEach((p: any) => currentUserOwnModules.set(p.code, p));
  }
  const psz = permissionCode.split(',');
  if (psz.length == 1) {
    return currentUserOwnModules.has(permissionCode);
  }
  for (const c of psz) {
    const has = currentUserOwnModules.has(c);
    if (!has) {
      //任何一个为false,则直接返回false;
      return false;
    }
  }
  return true;
}

/**
 * 将平面数组对象结构转为树形对象结构
 * @param list 对象数组,对象必须包含id(主键),parentId(父主键)
 * @param itemCallback 每项回调函数
 * @param isChangeOriginal 是否改变原始数据
 */
export function listToTree<T = {}>(list: T[], itemCallback?: ((p: T) => void), isChangeOriginal: boolean = false): TreeNodeModel[] {
  let map = {}, node: any, tree = [], newList = [], i: number;
  const idName = 'id';
  const parentIdName = 'parentId';
  const childrenName = 'children';
  for (i = 0; i < list.length; i++) {
    let item = list[i];
    if (!isChangeOriginal) {
      item = {...item};
    }
    const id = item[idName];
    map[id] = item;
    newList.push(item);
    // list[i][childrenName] = [];
  }
  for (i = 0; i < newList.length; i += 1) {
    node = newList[i];
    if (itemCallback) {
      itemCallback(node);
    }
    const pid = node[parentIdName]
    if (map[pid]) {
      if (!map[pid][childrenName]) map[pid][childrenName] = [];
      map[pid][childrenName].push(node);
    } else {
      tree.push(node);
    }
  }
  return tree;
}

/**
 * 将树形对象结构转为平面数组对象结构
 * @param treeData 树形对象结构
 * @param itemCallback 每项回调函数
 */
export function treeToList(treeData: TreeNodeModel[], itemCallback?: ((p: any) => void)): any[] {
  const data: any[] = [];
  eachTree(treeData, current => {
    itemCallback && itemCallback(current);
    data.push(current);
  });
  return data;
}

/**
 * 递归删除指定标识的树节点以及所有下级节点
 * @param treeData 树集合
 * @param id 节点标识
 */
export function deleteTreeNodeById(treeData: any[], id: string) {
  for (let i = 0; i < treeData.length; i++) {
    const node = treeData[i];
    if (node.id === id) {
      treeData.splice(i, 1);
      return;
    } else if (node.children) {
      deleteTreeNodeById(node.children, id);
    }
  }
}

/**
 * 递归查找指定标识的树节点,找到节点后退出递归
 * @param treeData 树集合
 * @param key 节点标识
 * @param callback 回调函数 参数 currentNode-当前节点对象; currentIndex 当前节点序号; currentArray 当前数组
 */
export const findTreeNodeByKey = (treeData: TreeNodeModel[], key: string, callback: (currentNode: TreeNodeModel, currentIndex: number, currentArray: TreeNodeModel[]) => void) => {
  for (let i = 0; i < treeData.length; i++) {
    if (treeData[i].key === key) {
      callback(treeData[i], i, treeData);
      return true;
    } else if (treeData[i].children) {
      if (findTreeNodeByKey(treeData[i].children, key, callback)) {
        return true;
      }
    }
  }
  return false;
};


/**
 * 递归查找指定标识的树节点,如果能找到,返回节点对象,否则返回null
 * @param treeData 树集合
 * @param key 节点标识
 */
export const getTreeNodeByKey = (treeData: TreeNodeModel[], key: string): TreeNodeModel | null => {
  for (let i = 0; i < treeData.length; i++) {
    if (treeData[i].key === key) {
      return treeData[i];
    } else if (treeData[i].children) {
      const n = getTreeNodeByKey(treeData[i].children, key);
      if (n !== null) {
        return n;
      }
    }
  }
  return null;
};

/**
 * 查找所有根节点集合
 * @param treeData Tree节点数组
 */
export function findRootNodes(treeData: TreeNodeModel[]) {
  let map = {}, node: TreeNodeModel, rootNodes: TreeNodeModel[] = [];
  for (let i = 0; i < treeData.length; i++) {
    let item = treeData[i];
    const id = item.id;
    map[id] = item;
  }
  for (let i = 0; i < treeData.length; i++) {
    node = treeData[i];
    const pid = node.parentId;
    if (!map[pid]) {
      rootNodes.push(node);
    }
  }
  return rootNodes;
}


function _findParentNodesByKey(treeData: TreeNodeModel[],
                               condition: (current: TreeNodeModel) => boolean,
                               parentNodes: TreeNodeModel[] = []): TreeNodeModel[] {
  if (!treeData) return []
  for (const item of treeData) {
    parentNodes.push(item)
    if (condition(item)) return parentNodes;
    if (item.children) {
      const findChildren = _findParentNodesByKey(item.children, condition, parentNodes);
      if (findChildren.length) return findChildren;
    }
    parentNodes.pop();
  }
  return [];
}


/**
 * 查找指定节点的所有上级节点集合
 * @param treeData Tree节点数组
 * @param key 节点标识
 * @param hasSelf 是否包含自身
 */
export function findParentNodesByKey(treeData: TreeNodeModel[], key: string, hasSelf: boolean = true) {
  if (hasSelf) {
    return _findParentNodesByKey(treeData, item => item.key === key);
  }
  const node = getTreeNodeByKey(treeData, key);
  if (node === null) return [];
  return _findParentNodesByKey(treeData, item => item.key === node.parentId);
}

function _findChildrenNodesByKey(treeData: TreeNodeModel[], childrenNodes: TreeNodeModel[]) {
  if (!treeData) return;
  for (const item of treeData) {
    childrenNodes.push(item);
    if (item.children) {
      _findChildrenNodesByKey(item.children, childrenNodes);
    }
  }
}

/**
 * 查找指定节点的所有下级节点集合
 * @param treeData Tree节点数组
 * @param key 节点标识
 * @param hasSelf 是否包含自身
 */
export function findChildrenNodesByKey(treeData: TreeNodeModel[], key: string, hasSelf: boolean = true) {
  const childrenNodes: TreeNodeModel[] = [];
  const node = getTreeNodeByKey(treeData, key);
  if (node === null) return childrenNodes;
  if (hasSelf) childrenNodes.push(node);
  _findChildrenNodesByKey(node.children, childrenNodes);
  return childrenNodes;
}

/**
 * 查找所有叶子节点(最末的节点)
 * @param treeData Tree节点数组
 */
export function findLeafNodes(treeData: TreeNodeModel[]) {
  const leafNodes: TreeNodeModel[] = [];
  eachTree(treeData, current => {
    if (!current.children || current.children.length == 0) {
      leafNodes.push(current);
    }
  });
  return leafNodes;
}

function _filterTreeCore(treeMap: Map<string, TreeNodeModel>, pid: string, resultMap: Map<string, TreeNodeModel>) {
  const node = treeMap.get(pid);
  if (node !== undefined) {
    resultMap.set(node.id, node);
    _filterTreeCore(treeMap, node.parentId, resultMap);
  }
}

/**
 * 过滤树集合
 * @param treeData 树对象集合
 * @param condition 过滤条件
 * @param resultItemCallback 搜索结果项回调函数
 */
export function filterTree(treeData: TreeNodeModel[],
                           condition: (current: TreeNodeModel) => boolean,
                           resultItemCallback?: (current: TreeNodeModel) => void) {
  const list = treeToList(treeData);
  const treeMap: Map<string, TreeNodeModel> = new Map<string, TreeNodeModel>();
  const resultMap: Map<string, TreeNodeModel> = new Map<string, TreeNodeModel>();
  const filterList: TreeNodeModel[] = [];

  for (const item of list) {
    treeMap.set(item.id, item);
    if (condition(item)) {
      filterList.push(item);
    }
  }

  for (const item of filterList) {
    resultMap.set(item.id, item);
    const pid = item.parentId;
    _filterTreeCore(treeMap, pid, resultMap);
  }

  if (resultMap.size > 0) {
    const targetList: TreeNodeModel[] = [];
    for (let item of resultMap.values()) {
      const {children, ...rest} = item;
      const rn = rest as TreeNodeModel;
      resultItemCallback && resultItemCallback(rn);
      targetList.push(rn);
    }
    return listToTree(targetList);
  }
  return [];
}

/**
 * 递归树集合
 * @param treeData 树集合
 * @param callback 每个节点回调函数 参数 current-当前节点
 */
export function eachTree(treeData: TreeNodeModel[], callback: (current: TreeNodeModel) => void) {
  for (let i = 0; i < treeData.length; i++) {
    const node = treeData[i];
    callback(node);
    if (node.children) {
      eachTree(node.children, callback);
    }
  }
}


/**
 * 保存拖拽数据
 */
function saveDndData(children: TreeNodeModel[], parentId: string, onUpdateDnd?: (dndNodes: DndNodeModel[]) => Promise<ApiResult>) {
  let dndData: DndNodeModel[] = [];
  for (let j = 0; j < children.length; j++) {
    const current = children[j];
    current.parentId = parentId;
    current.path = j;
    dndData.push({id: current.id, parentId: current.parentId, index: current.path})
  }
  if (dndData.length > 0 && onUpdateDnd) {
    onUpdateDnd(dndData).then(() => {
    });
  }
}

/**
 * 树拖拽逻辑
 * @param dropInfo 拖拽节点信息
 * @param treeData 树集合
 * @param setTreeData 设置树集合
 * @param onUpdateDnd 更新拖拽数据回调函数 参数:dndNodes-拖拽节点信息集合
 */
export function onTreeDrop(dropInfo: any,
                           treeData: TreeNodeModel[],
                           setTreeData: Dispatch<SetStateAction<TreeNodeModel[]>>,
                           onUpdateDnd?: (dndNodes: DndNodeModel[]) => Promise<ApiResult>
) {
  const dragKey = dropInfo.dragNode.key;
  const dropKey = dropInfo.node.key;
  const dropPos = dropInfo.node.pos.split('-');
  const dropPosition = dropInfo.dropPosition - Number(dropPos[dropPos.length - 1]);
  const data = [...treeData];

  let dragNode: TreeNodeModel;
  findTreeNodeByKey(data, dragKey, (currentNode, currentIndex, currentArray) => {
    //记录当前拖拽节点
    dragNode = currentNode;
    //移除拖拽的节点
    currentArray.splice(currentIndex, 1);
  });

  if (!dropInfo.dropToGap) {
    //拖拽成为某个节点的子节点
    findTreeNodeByKey(data, dropKey, (currentNode, currentIndex, currentArray) => {
      currentNode.children = currentNode.children || [];
      //将拖拽的节点添加到目标节点的子节点末尾
      currentNode.children.push(dragNode);
      //保存拖拽数据
      saveDndData(currentNode.children, currentNode.id, onUpdateDnd);
    });
  } else {
    //拖拽节点到其他节点的上面或者下面
    findTreeNodeByKey(data, dropKey, (currentNode, currentIndex, currentArray) => {
      let pos = dropPosition === -1 ? currentIndex : currentIndex + 1;
      //将拖拽的节点添加到目标节点的上面或者下面
      currentArray.splice(pos, 0, dragNode);
      //保存拖拽数据
      saveDndData(currentArray, currentNode.parentId, onUpdateDnd);
    });
  }
  setTreeData(data);
}


/**
 * 树展开逻辑
 * @param nodeKey 节点标识
 * @param nodeExpanded 节点展开状态
 * @param expandedKeys 展开的节点数组
 * @param setExpandedKeys 更新展开的节点数组回调
 */
export function onTreeExpand(nodeKey: string,
                             nodeExpanded: boolean,
                             expandedKeys: string[],
                             setExpandedKeys: Dispatch<SetStateAction<string[]>>
) {
  const index = expandedKeys.findIndex(p => p === nodeKey);
  if (!nodeExpanded && index > -1) {
    //删除
    setExpandedKeys(o => {
      o.splice(index, 1);
      return o;
    });
  } else if (nodeExpanded && index == -1) {
    //添加
    setExpandedKeys(o => {
      o.push(nodeKey);
      return o;
    });
  }
}

/**
 * 新创建延迟函数
 * @param delayTime 延迟毫秒数,默认500ms;
 * @return 返回延迟执行对象
 * 延迟执行对象包括三个函数:
 * run:运行需要延迟执行的程序,通过参数 callback 传入,此函数会取消掉之前的延时函数,创建新的延迟函数
 * cancel:取消run函数创建的延迟函数
 * finish:取消run函数创建的延迟函数,并可以通过参数 callback 传入需要清理的代码
 */
export function newDelay(delayTime: number = 500): {
  run: (callback: () => void) => void,
  cancel: () => void,
  finish: (callback: () => void) => void
} {
  let loadingDelayTimer: any = undefined;
  const cancel = function () {
    if (loadingDelayTimer) {
      clearTimeout(loadingDelayTimer);
    }
  };
  const finish = function (callback: () => void) {
    cancel();
    callback && callback();
  };
  const run = function (callback: () => void) {
    cancel();
    if (delayTime > 0) {
      loadingDelayTimer = setTimeout(() => {
        callback();
      }, delayTime);
    }
  };

  return {run, cancel, finish};
}

/** ajax导出Excel数据 */
export function exportExcel(url: string, data: any) {
  request.post(url, {
    data,
    responseType: 'blob',
    getResponse: true,
  }).then(({data, response}) => {
    const disposition = response.headers.get('content-disposition') || '';
    const fileName = decodeURI(disposition.substring(disposition.indexOf('filename=') + 9, disposition.length));
    const link = document.createElement('a');
    let blob = new Blob([data], {type: 'application/vnd.ms-excel'});
    link.style.display = 'none';
    const href = URL.createObjectURL(blob);
    link.href = href;
    link.setAttribute('download', fileName);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    window.URL.revokeObjectURL(href); //释放掉blob对象
  });
}

/** 导出数据 */
export function exportWeb(url: string, data: any) {
  const syo = url.match('[\?]') ? '&' : '?';
  for (let key of Object.keys(data)) {
    const val = data[key];
    if (val == null) {
      data[key] = null;
    }
    if (typeof val === 'string' && (val === 'null' || val === 'undefined')) {
      data[key] = null;
    }
  }
  const search = qs.stringify(data, {skipNulls: true});
  if (search) {
    window.location.href = url + syo + search;
  } else {
    window.location.href = url;
  }
}

/**
 * 获取状态枚举
 * @param hasAll 默认有全部选项，传false去掉全部
 */
export function getStatusEnum(hasAll: boolean = true) {
  let map;
  if (!hasAll) {
    map = {'false': '禁用', 'true': '启用'};
  } else {
    map = {'null': '全部状态', 'false': '禁用', 'true': '启用'};
  }
  return map;
}

/**
 * 根据数据字典生成ValueEnum
 * @param categoryCode 字典编码
 */
export async function buildDicValueEnum(categoryCode: string) {
  const cenum = {};
  const result = await dicService.selectListByCategoryCode(categoryCode);
  result.data.map(p => cenum[p.value] = p.name);
  return cenum;
}

/** 日期格式化 格式:YYYY-MM-DD */
export function formatDate(date: moment.Moment) {
  return date.format('YYYY-MM-DD');
}

/** 日期时间格式化 格式:YYYY-MM-DD HH:mm:SS */
export function formatDateTime(date: moment.Moment) {
  return date.format('YYYY-MM-DD HH:mm:SS');
}

/** 时间格式化 格式:HH:mm:SS */
export function formatTime(date: moment.Moment) {
  return date.format('HH:mm:SS');
}

/** 时间格式化 格式:HH:mm */
export function formatHourMin(date: moment.Moment) {
  return date.format('HH:mm');
}

/**勾选图标列 */
export function checkedFaColumnRender(checked: boolean) {
  return checked && (<i style={{color: 'green'}} className="fal fa-check"/>);
}

const DeptScopeNames = {
  1: "全部",
  2: "自定义",
  3: "所在机构及所有下级",
  4: "所在机构",
  5: "仅本人"
};

/** 获取机构数据权限名称 */
export function getDeptScopeName(value: number) {
  const val = DeptScopeNames[value];
  if (!val) return '';
  return val;
}

/**
 * 根据字典值类型转换字典值
 * @param value 字典值
 * @param type 字典值类型
 */
export function convertDicValueByType(value: any, type: DicValueType) {
  switch (type) {
    case DicValueType.string:
      return String(value);
    case DicValueType.number:
      return Number(value);
  }
}

/** 布尔转数字 */
export function booleanToNumber(val: boolean | undefined) {
  if (val === undefined) return 0;
  return val ? 1 : 0;
}

/** 数字转布尔 */
export function numberToBoolean(val: number | undefined) {
  if (val === undefined) return false;
  return val != 0;
}
